HihoCoder 1279 Rikka with Sequence(状压dp)
题意:
$给定N\le 50个整数,A_i\in[0,2^{13})$
$从中选取若干个数(不为0)使得bitwise and的结果和bitwise xor的结果相同$
$求方法数$
分析:
$首先根据and和xor运算的性质进行分析,如果要相等,以二进制位来看:$
$为0的话,要有偶数个1,并且有至少1个0$
$为1的话,要有奇数个1,并且没有0$
$显然一眼状压dp,偶数个1,奇数个1,全为1还是不全为1,状态数4
^{13}炸了$
$考虑优化,反正我是想不到,三进制状压:$
$0:=偶数个1且不全为1,1:=奇数个1且不全为1,2:=全为1$
$再开1维,0/1:=选取了偶/奇数个数$
$然后你奇妙的发现这包含了上面的四种状态,状态数3^{13}×2,完美$
$f[i][S][0/1]:=前i个数,选取的数的状态是S,选取的数的个数是奇/偶的方法数$
$转移就01背包转移就好了,这个数选还是不选$
$由于转移是O(13)的,出题人说常数卡的好能过,窝折半预处理到O(1)特么的还是T了一晚上$
$卡了半天常数才过,最后窝发现并不是卡常数,是特么的不滚动就要T,估计是MLE给搞T了$
$能滚动就滚动吧,别偷懒了$
$时间复杂度O(n×3^{13})$
代码:
//
// Created by TaoSama on 2016-03-21
// Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
int n;
typedef long long LL;
const int S = 1594323; //3^13
LL f[2][S][2];
// S: 0->偶数个1(不全是) 1->奇数个1(不全是) 2->全是1
// 0/1: 选取了偶/奇数个数
int sta[13];
inline void decode(int s, int n) {
for(int i = 0; i < n; ++i) {
sta[i] = s % 3;
s /= 3;
}
}
inline int encode(int n) {
int code = 0;
for(int i = n - 1; ~i; --i) code = code * 3 + sta[i];
return code;
}
const int HS = 2187; //3^7
int trans[HS][1 << 7][2];
int sHigh[S], sLow[S];
int th[13] = {1};
void gao() {
for(int i = 1; i < 13; ++i) th[i] = th[i - 1] * 3;
for(int j = 0; j < S; ++j) sHigh[j] = j / HS, sLow[j] = j % HS;
for(int i = 0; i < HS; ++i) {
for(int j = 0; j < 1 << 7; ++j) {
for(int p = 0; p < 2; ++p) {
int newS = i;
for(int k = 0; k < 7; ++k) {
int b = i / th[k] % 3;
if(j >> k & 1) { //1
if(b != 2) {
//sta[k] ^= 1; //奇偶互换
newS += (b ? -1 : 1) * th[k];
}
} else { //0
if(b == 2) {
//sta[k] = p; //不全是1了
newS += (p - 2) * th[k];
}
}
}
//trans[i][j][p] = encode(7);
trans[i][j][p] = newS;
}
}
}
}
inline bool check(int s, int p) {
decode(s, 13);
//偶数个1(不全是) 同为0, 奇数个1(全是) 同为1
//非法的同理
for(int i = 0; i < 13; ++i)
if(sta[i] == 1 || !p && sta[i] == 2) return false;
return true;
}
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
gao();
scanf("%d", &n);
int p = 0;
f[p][S - 1][0] = 1;
for(int i = 1; i <= n; ++i) {
int x; scanf("%d", &x);
int xHigh = x / (1 << 7), xLow = x % (1 << 7);
memset(f[!p], 0, sizeof f[!p]);
for(int j = 0; j < S; ++j) {
for(int k = 0; k < 2; ++k) {
if(!f[p][j][k]) continue;
int newHigh = trans[sHigh[j]][xHigh][k];
int newLow = trans[sLow[j]][xLow][k];
int newS = newHigh * HS + newLow;
f[!p][newS][k ^ 1] += f[p][j][k]; //选
// pr(i); pr(j); pr(k); prln(x);
// pr(sLow); pr(xLow); pr(sHigh); prln(xHigh);
// pr(newHigh); pr(newLow); prln(newS);
// decode(j, 13);
// for(int k = 0; k < 13; ++k) printf("%d ", sta[k]); puts("");
// decode(newS, 13);
// for(int k = 0; k < 13; ++k) printf("%d ", sta[k]); puts("");
// if(i == n) printf("f[%d][%d]=%I64d\n", newS, k ^ 1, f[i][newS][k ^ 1]);
f[!p][j][k] += f[p][j][k]; //不选
}
}
p = !p;
}
LL ans = 0;
for(int i = 0; i < S; ++i)
for(int j = 0; j < 2; ++j)
if(f[p][i][j] && check(i, j)) ans += f[p][i][j];
printf("%lld\n", ans);
return 0;
}